package entities.role;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import option.KBVocabulary;

import kb.RBox;
import entities.KBEntity;
import entities.concept.Concept;

/**
 * Only simple/inverse roles are considered at the moment A role has only one
 * inverse role.
 * 
 * @author j55wu
 * 
 */
public final class Role implements KBEntity{

	private final String rolename;
	private Concept domain = null, range = null;
	private static Map<String, Role> roleMaps = new HashMap<String, Role>();
	private Set<Concept> compDom, compRan; //domain and range for all superroles
	
	
	public static Role createRoleAsIs(String name){
		if (roleMaps.containsKey(name)) {
			return roleMaps.get(name);
		}else{
			Role r = new Role(name);
			roleMaps.put(name, r);
			return r;
		}
	}

	//this constructor is reserved for parsers: one can simply write inv-R to denote the inverse of R.
	public static Role createRole(String name, RBox rb) {
		if (roleMaps.containsKey(name)) {
			return roleMaps.get(name);
		}
		//if a role name starts with inv-: see if its inverse is already there
		Role r = null;
		if(name.startsWith(KBVocabulary.INV_ROLE_PREFIX)){
			String op = name.substring(KBVocabulary.INV_ROLE_PREFIX.length());
			Role oprole = Role.createRoleAsIs(op);
			if(rb != null){
				r = rb.getInverseRole(oprole);
			}
		}
		if(r == null){
			r = createRoleAsIs(name);
		}
		return r;
	}
	
	public Set<Concept> getCompleteDomain(RBox rb){
		//need to add the domain of this role and all superroles!
		if(this.compDom != null){
			return this.compDom;
		}
		//init the complete domain
		this.compDom = new HashSet<Concept>();
		Set<Role> drs = new HashSet<Role>();
		if(rb.getAllSuperRoles(this) == null){
			drs.add(this);
		}else{
			drs.addAll(rb.getAllSuperRoles(this));
		}
		for(Role dr : drs){
			Concept dc = dr.getDomain();
			if( dc != null ){
				this.compDom.add(dc);
			}
		}
		//ALSO: need to add the range of inv-this-role (ir) and the range of all superroles of ir
		Role ir = rb.getInverseRole(this);
		drs = new HashSet<Role>();
		if(rb.getAllSuperRoles(ir) == null){
			drs.add(ir);
		}else{
			drs.addAll(rb.getAllSuperRoles(ir));
		}
		for(Role dr : drs){
			Concept dc = dr.getRange();
			if( dc != null ){
				this.compDom.add(dc);
			}
		}
		return this.compDom;
	}
	
	public Set<Concept> getCompleteRange(RBox rb){
		//need to add the domain of this role and all superroles!
		if(this.compRan != null){
			return this.compRan;
		}
		//init the complete domain
		this.compRan = new HashSet<Concept>();
		Set<Role> rrs = new HashSet<Role>();
		if(rb.getAllSuperRoles(this) == null){
			rrs.add(this);
		}else{
			rrs.addAll(rb.getAllSuperRoles(this));
		}
		for(Role rr : rrs){
			Concept rc = rr.getRange();
			if( rc != null ){
				this.compRan.add(rc);
			}
		}
		//need to add the domain of inv-this-role (ir) and the domain of all superroles of ir
		rrs = new HashSet<Role>();
		Role ir = rb.getInverseRole(this);
		if(rb.getAllSuperRoles(ir) == null){
			rrs.add(ir);
		}else{
			rrs.addAll(rb.getAllSuperRoles(ir));
		}
		for(Role rr : rrs){
			Concept rc = rr.getDomain();
			if( rc != null ){
				this.compRan.add(rc);
			}
		}
		return this.compRan;
	}

	private Role(String name) {
		this.rolename = name;
	}

	public String getRoleName() {
		return this.rolename;
	}

	public void setDomain(Concept dm) {
		if (domain == null) {
			this.domain = dm;
		} else {
			// conjunction of new domain
			Set<Concept> cons = new HashSet<Concept>();
			cons.add(this.domain);
			cons.add(dm);
			this.domain = Concept.getConjunctiveConcept(cons);
		}
	}

	public void setRange(Concept rg) {
		if (this.range == null) {
			this.range = rg;
		} else {
			// conjunction of new range
			Set<Concept> cons = new HashSet<Concept>();
			cons.add(this.range);
			cons.add(rg);
			this.range = Concept.getConjunctiveConcept(cons);
		}
	}

	public Concept getDomain() {
		return this.domain;
	}

	public Concept getRange() {
		return this.range;
	}

	private volatile int hashCode = 0;

	@Override
	public int hashCode() {
		if (hashCode != 0) {
			return hashCode;
		}
		final int prime = 31;
		int result = 17;
		result = prime * result
				+ ((rolename == null) ? 0 : rolename.hashCode());
		hashCode = result;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (!(obj instanceof Role))
			return false;
		Role other = (Role) obj;
		if (rolename == null) {
			if (other.rolename != null)
				return false;
		} else if (!rolename.equals(other.rolename))
			return false;
		return true;
	}

	public Role getPrimeCopy() {
		return createRoleAsIs(this.rolename + Concept.PRIME);
	}

	@Override
	public String toString() {
		// String[] prefix ={
		// "http://www.lehigh.edu/~zhp2/2004/0401/univ-bench.owl#",
		// "http://www.Department0.University0.edu/"
		// };
		// for(String p:prefix){
		// if(rolename.contains(p)){
		// return rolename.replaceAll(p, "");
		// }
		// }
		return rolename;
	}

	public String toSerializedString() {
		return " (" + KBVocabulary.ROLE + " " + this.rolename + ")";
	}

	public Set<KBEntity> extractSig(){
		Set<KBEntity> sig = new HashSet<KBEntity>();
		sig.add(this);
		return sig;
	}
}
